#!/usr/bin/env python

# Copyright (C) 2003-2007  Robey Pointer <robeypointer@gmail.com>
#
# This file is part of paramiko.
#
# Paramiko is free software; you can redistribute it and/or modify it under the
# terms of the GNU Lesser General Public License as published by the Free
# Software Foundation; either version 2.1 of the License, or (at your option)
# any later version.
#
# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
# details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with Paramiko; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.


import base64
from binascii import hexlify
import getpass
import os
import select
import socket
import sys
import time
import traceback
from paramiko.py3compat import input
import termios
import signal
import fcntl
import struct
import paramiko
import subprocess
from backend import  utils
from backend import audit
from repository import models

try:
    import interactive
except ImportError:
    from . import interactive


def agent_auth(transport, username):
    """
    尝试使用任何私有身份对给定的传输进行身份验证可从SSH代理键.
    """
    
    agent = paramiko.Agent()
    agent_keys = agent.get_keys()
    if len(agent_keys) == 0:
        return
        
    for key in agent_keys:
        print('Trying ssh-agent key %s' % hexlify(key.get_fingerprint()))
        try:
            transport.auth_publickey(username, key)
            print('... success!')
            return
        except paramiko.SSHException:
            print('... nope.')


def manual_auth(ins,username, hostname,pw,host_obj,main_ins):

    #auth = input('Auth by (p)assword, (r)sa key, or (d)ss key? [%s] ' % default_auth)
    # if host_obj.host_user.auth_method == 'ssh-key':
    if host_obj.auth_type == '1':
        #default_path = os.path.join(os.environ['HOME'], '.ssh', 'id_rsa')
        #path = input('RSA key [%s]: ' % default_path)
        #if len(path) == 0:
        path = main_ins.django_settings.RSA_PRIVATE_KEY_FILE

        if not os.path.isfile(path):
            sys.exit("\033[31;1mError:RSA private key file [%s] doesn't exist, please make sure you have set your RSA correctly.\033[0m" % path )
        try:
            key = paramiko.RSAKey.from_private_key_file(path)

        except paramiko.PasswordRequiredException:
            password = getpass.getpass('RSA key password: ')
            key = paramiko.RSAKey.from_private_key_file(path, password)
        ins.auth_publickey(username, key)

    else:
        ins.auth_password(username, pw)


def get_session_id(instance_self,bind_host_obj,tag):
        ''' 得到一个  session会话 id '''
        #print('==instance_self==',instance_self.user_id,bind_host_obj,tag)
        session_obj = models.SessionTrack(user_id=instance_self.user_id,bind_host=bind_host_obj,tag=tag)
        session_obj.save()
        return session_obj

def login_raw(instance,h):
    '''此登录方式,是alex Crazy_eye 2.0 中跳板机使用原生的ssh 登录其他的机器，ssh -E 标志位tag ,在seesion_tracker.sh
       中使用 strace -p '进程号' 然后记录下你的操作命令。并分析并存入数据库。
    '''

    ip, port, username, password = h.host.private_ip, h.host.port, h.remote_user, h.password

    ssh_tag = utils.random_str(16)
    session_obj = get_session_id(instance,h,ssh_tag)
    print('==instance.django_settings.BASE_DIR==',instance.django_settings.BASE_DIR)
    print('==session_obj.id==',session_obj.id)
    print('==ssh_tag==',ssh_tag)

    session_track_process = subprocess.Popen(
        "/usr/bin/sh  %s/backend/session_tracker.sh  %s  %s" % (instance.django_settings.BASE_DIR,session_obj.id,ssh_tag),
        shell=True,
        stdout =subprocess.PIPE,
        stderr = subprocess.PIPE
    )
    # print(session_track_process.stderr.read())

    ##########################################

    cmd_str = "sshpass -p %s ssh  %s@%s -p%s -E %s -o StrictHostKeyChecking=no" %(password,username,ip,port,ssh_tag)
    #ssh -E 的作用估计只有在 centos7 中使用。
    subprocess.run(cmd_str,shell=True)

    #更新 ssh 一次连接 到 断开后的会话保持时间 和执行命令的个数.
    print ('========update session stay time======')
    session_obj.stay_time = time.time() -  session_obj.date.timestamp()
    session_log_file = "%s/%s/session_%s.log" % (instance.django_settings.SESSION_AUDIT_LOG_DIR,
                                    session_obj.date.strftime( "%Y_%m_%d"),
                                    session_obj.id
                                    )
    log_parser = audit.AuditLogHandler(session_log_file)
    log_data = log_parser.parse()
    session_obj.cmd_count = len(log_data)
    session_obj.save()

##############################################################################

def login(main_ins,h):
    '''
    此登录方式,是alex Crazy_eye 1.0 中跳板机使用 python parammiko模块 源码中的ssh_interactive.py 和 demo.py
    登录远程主机并记录操作操作日志，并把命令保存到数据库。
    '''

    ip,port,username,password=h.host.private_ip,h.host.port,h.remote_user,h.password
    print(ip,port,username,password)

    # now connect
    try:
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.settimeout(15)
        sock.connect((ip, port))
    except Exception as e:
        print('*** 连接失败: ' + str(e))
        main_ins.flush_cmd_input(str(e),h,1)
        main_ins.flush_cmd_input('--session closed--',h,2)

        #traceback.print_exc()
        return    #sys.exit(1)

    try:
        t = paramiko.Transport(sock)
        try:
            t.start_client()
        except paramiko.SSHException:
            print('*** SSH negotiation failed.')
            sys.exit(1)

        try:
            keys = paramiko.util.load_host_keys(os.path.expanduser('~/.ssh/known_hosts'))
        except IOError:
            try:
                keys = paramiko.util.load_host_keys(os.path.expanduser('~/ssh/known_hosts'))
            except IOError:
                print('*** Unable to open host keys file')
                keys = {}

        agent_auth(t, username)
        if not t.is_authenticated():
            manual_auth(t, username, ip,password,h,main_ins)
        if not t.is_authenticated():
            print('*** Authentication failed. :(')
            t.close()
            sys.exit(1)

        chan = t.open_session()
        # t_height,t_width = get_terminal_size()
        # chan.get_pty(term='xterm',height=t_height,width=t_width)
        chan.get_pty()
        chan.invoke_shell()

        print('*** 登录 成功 ***\n')
        main_ins.flush_cmd_input('---- Logged in! ----',h,1)
        main_ins.flush_audit_cmd_log(h)
        # try:
        #     signal.signal(signal.SIGWINCH, get_terminal_size)
        # except:
        #     pass
        interactive.interactive_shell(chan,main_ins,ip,username,h) #  登录后的ssh 的交互界面 zhixing primiko mokuai de  def posix_shell():
        chan.close()
        t.close()

    except Exception as e:
        print('\033[31;1m%s\033[0m' % str(e))
        main_ins.flush_cmd_input(str(e),h,1)
        main_ins.flush_cmd_input('--session closed--',h,2)

        traceback.print_exc()
        try:
            t.close()
        except:
            pass
        sys.exit(1)


if __name__ == '__main__':
    login('192.168.2.250', 22,'alex','alex3714')

